package ldh.text.service;

import ldh.text.exception.TooLongFinderException;
import ldh.text.util.RegexUtil;
import ldh.text.vo.FindPosition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Pattern;

public class FinderService {

    private static final Logger LOGGER = LoggerFactory.getLogger(FinderService.class);

    private static final int MAX_SIZE = 50000000; // 50W
    private static final int cacheSize = 10 * 1024 * 1024;

    private Pattern pattern = null;
    private CursorDataService<ByteBuffer> cacheDataService;

    public void find(File file, String findStr, Consumer<List<FindPosition>> findResult) throws IOException {
        cacheDataService = new CacheDataService(file, cacheSize);
        doFind(findStr, 100, findResult);
        cacheDataService.close();
    }

    public void find(File file, String findStr, int aroundSize, Consumer<List<FindPosition>> findResult) throws IOException {
        cacheDataService = new CacheDataService(file, cacheSize);
        doFind(findStr, aroundSize, findResult);
        cacheDataService.close();
    }

    public void find2(File file, String findStr, Consumer<List<FindPosition>> findResult) throws IOException {
        cacheDataService = new Cache2DataService(file, cacheSize);
        doFind(findStr, 100, findResult);
        cacheDataService.close();
    }

    private void doFind(String findStr, int aroundSize, Consumer<List<FindPosition>> findResult) throws IOException {
        pattern = Pattern.compile(findStr);
        long total = 0;
        ByteBuffer byteBuffer = cacheDataService.loadData(0, cacheSize);
        if (byteBuffer.limit() < 1) return;
        total += byteBuffer.limit();
        String content = new String(byteBuffer.array(), 0, byteBuffer.limit(), "utf8");
        List<FindPosition> result = new ArrayList<>();
        RegexUtil.find(pattern, content, result, aroundSize);
        findResult.accept(result);
        while (byteBuffer.limit() >0) {
            byteBuffer = cacheDataService.loadNextData(cacheSize);
            if (byteBuffer.limit() < 1) break;
            total += byteBuffer.limit();
            content = new String(byteBuffer.array(), 0, byteBuffer.limit(), "utf8");
            result = new ArrayList<>();
            RegexUtil.find(pattern, content, result, aroundSize);
            findResult.accept(result);
        }
        LOGGER.debug("total {}", total);
    }

    public List<FindPosition> find(File file, String findStr) throws IOException {
        List<FindPosition> result = new ArrayList<>();
        find(file, findStr, findResults->{
            result.addAll(findResults);
            if (result.size() > MAX_SIZE) {
                throw new TooLongFinderException("查找结果太多，请优化查找关键词");
            }
        });
        return result;
    }

    public List<FindPosition> find2(File file, String findStr) throws IOException {
        List<FindPosition> result = new ArrayList<>();
        find2(file, findStr, findResults->{
            result.addAll(findResults);
            if (result.size() > MAX_SIZE) {
                throw new TooLongFinderException("查找结果太多，请优化查找关键词");
            }
        });
        return result;
    }
}
